package fpuna.ia.ea;

/*
 * 15/11/2011
 */

import java.io.*;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

/**
 *
 * @author gusamasan
 */
public class FrentePareto {
// ----------------------------------------------------------------------------

    private String contenedorNombresArchivosSalida;
    private LinkedList puntosFrente;

// ----------------------------------------------------------------------------

    /** Constructores *********************************************************/
    public FrentePareto(){
        this.puntosFrente = new LinkedList();

        this.contenedorNombresArchivosSalida = "archivosDeSalida.txt";
    }

    public FrentePareto( String nombresArchivosSalida ){
        this.puntosFrente = new LinkedList();

        this.contenedorNombresArchivosSalida = nombresArchivosSalida;
    }
    /**************************************************************************/

    /**
     * Agrega un nuevo punto al FrentePareto solamente si dicho punto es no
     * dominado
     * 
     * @param   instancia de la clase <code>Punto</code> que representa el nuevo
     *          punto a agregar
     */
    public void agregarSiNoDominado( Punto puntoNuevo ){
    // ------------------------------------------------------------------------

        int cantidadElementos;

        boolean agregar;

        Iterator iterador;

        Punto   puntoDeFrente;

    // ------------------------------------------------------------------------

        //System.out.println( puntoNuevo.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_UNO ));
        if( this.puntosFrente.size() > 0 ){
          
            cantidadElementos   = this.puntosFrente.size();

            agregar     = true;

            while( cantidadElementos > 0 ){
                puntoDeFrente   = (Punto)this.puntosFrente.removeFirst();//Punto)iterador.next();

                if( puntoDeFrente.dominaA( puntoNuevo ) ){
                    this.puntosFrente.add( puntoDeFrente );
                    agregar = false;
                    break;
                }
                else if( !puntoNuevo.dominaA( puntoDeFrente ) ){
                    this.puntosFrente.add( puntoDeFrente );
                }

                cantidadElementos--;
            }

            if( agregar ){
                this.puntosFrente.add( puntoNuevo );
            }
           
        }
        else{
            this.puntosFrente.add( puntoNuevo );

        }

    }

    /**
     * Carga un FrentePareto que representa al Ytrue.
     * Para realizar la carga, lee los nombres de archivos que contienen resultados
     * o frentes de otros algoritmos. Los nombres de los archivos son especificados
     * en archivosDeSalida.txt
     */
    public void cargarYTrue(){
    // ------------------------------------------------------------------------

        int indice, limite;
        
        String archivos, linea, vectorArchivos[], vectorValores[];

        BufferedReader input;

        File archivo;

        Punto puntoNuevo;

    // ------------------------------------------------------------------------

        archivo     = new File( FrentePareto.class.getClassLoader().getResource( "fpuna/ia/ea/recurso/salida/" + this.contenedorNombresArchivosSalida ).getFile() );

        input       = null;

        archivos    = "";
        
        try{

            // Se leen los nombres de archivos que contienen las salidas
            // el resultado se carga en un vectorArchivos
            input =  new BufferedReader(new FileReader( archivo ) );

            while ( ( linea = input.readLine() ) != null){

                linea   = linea.trim();
                archivos= archivos + linea + ",";                
            }

            input.close();
            
            vectorArchivos  = archivos.split( "," );

            limite          = vectorArchivos.length;

            // Para cada archivo de salida, se leen los datos y se agrega
            // al Ytrue si el dato es no dominado
            for( indice=0; indice < limite; indice++ ){
                archivo     = new File( FrentePareto.class.getClassLoader().getResource( "fpuna/ia/ea/recurso/salida/" + vectorArchivos[indice] ).getFile() );

                input       = new BufferedReader(new FileReader( archivo ) );

                linea       = input.readLine();

                while( linea != null ){
                    vectorValores   = linea.split( " " );

                    puntoNuevo      = new Punto( 2 );
                    puntoNuevo.asignarValorObjetivo( Punto.INDICE_OBJETIVO_UNO, Double.parseDouble( vectorValores[0] ) );
                    puntoNuevo.asignarValorObjetivo( Punto.INDICE_OBJETIVO_DOS, Double.parseDouble( vectorValores[1] ) );

                    this.agregarSiNoDominado( puntoNuevo );

                    linea   = input.readLine();
                }

                input.close();
            }
                        

        }
        catch( Exception e ){
          e.printStackTrace();
        }
        
    }

    /**
     * Retorna la cantidad de puntos en el FrentePareto
     * 
     * @return <code>int</code> que representa la cantidad
     */
    public int obtenerCantidadPuntoFrente(){
        return( this.puntosFrente.size() );
    }

    public Iterator obtenerPuntosFrente(){
        return( this.puntosFrente.iterator() );
    }

    public double obtenerProductoValoresPromedios(){
    // ----------------------------------------------------------------------

        int cantidadPuntos;
        
        double promedio1, promedio2;

        Iterator iterador;

        Punto unPunto;

    // ----------------------------------------------------------------------

        promedio1       = 1;
        promedio2       = 1;

        iterador        = this.puntosFrente.iterator();

        cantidadPuntos  = this.puntosFrente.size();

        while( iterador.hasNext() ){

            unPunto = (Punto)iterador.next();

            promedio1+= unPunto.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_UNO );
            promedio2+= unPunto.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_DOS );

        }



       return( promedio1*promedio2/(cantidadPuntos*cantidadPuntos) );
    }


    /**
     * Imprime, en la salida estAndar, los valores de los punto en el Frente
     */
    public void imprimirFrente(){
    // ------------------------------------------------------------------------

        Iterator iterador;

        Punto unPunto;

    // ------------------------------------------------------------------------

        System.out.println( "cantidad elementos: " + this.puntosFrente.size() );
        
        iterador    = this.puntosFrente.iterator();

        while( iterador.hasNext() ){
            unPunto = (Punto)iterador.next();

            System.out.print( unPunto.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_UNO ) + " " );
            System.out.println( unPunto.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_DOS ) );
        }
    }

    private double distancia( Punto puntoUno, Punto puntoDos ){
    // ------------------------------------------------------------------------

        double x, y, distancia;
        
    // ------------------------------------------------------------------------

        x = puntoUno.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_UNO );
        x-= puntoDos.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_UNO );
        x = x*x;

        y = puntoUno.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_DOS );
        y-= puntoDos.obtenerValorObjetivo( Punto.INDICE_OBJETIVO_DOS );
        y = y*y;

        distancia   = Math.sqrt( x + y );

        return( distancia );
    }

    public double calcularDistanciaYtrueM1( FrentePareto Ytrue ){
    // ------------------------------------------------------------------------

        double suma, distanciaMinima, nuevaDistancia;

        Iterator iterador, iteradorYtrue;

        Punto unPunto;
        
    // ------------------------------------------------------------------------

        iterador        = this.puntosFrente.iterator();        

        suma            = 0;
        
        while( iterador.hasNext() ){

            unPunto         = (Punto)iterador.next();

            iteradorYtrue   = Ytrue.obtenerPuntosFrente();

            distanciaMinima = -1;

            while( iteradorYtrue.hasNext() ){

                nuevaDistancia  = this.distancia(unPunto, (Punto)iteradorYtrue.next() );

                if( distanciaMinima > -1 ){
                    if( nuevaDistancia < distanciaMinima )
                        distanciaMinima = nuevaDistancia;
                }
                else
                    distanciaMinima = nuevaDistancia;
            }

            suma+= distanciaMinima;
        }


        return( suma/this.obtenerCantidadPuntoFrente() );
    }

    public double calcularDistribucionM2( double sigma ){
    // ------------------------------------------------------------------------

        int cantidad;
        Iterator p,q;
        Punto puntoP;

    // ------------------------------------------------------------------------

        p        = this.puntosFrente.iterator();

        cantidad = 0;

        while( p.hasNext() ){

            puntoP  = (Punto)p.next();

            q       = this.puntosFrente.iterator();

            while( q.hasNext() ){

                if( this.distancia( puntoP, (Punto)q.next() ) > sigma ){
                    cantidad++;
                }
            }
        }


        return( cantidad/(this.puntosFrente.size() - 1 ) );
    }

    public double calcularExtensionM3(){
    // ------------------------------------------------------------------------

        double extension, nuevaDistancia;
        Iterator p,q;
        Punto puntoP;

    // ------------------------------------------------------------------------

        p        = this.puntosFrente.iterator();

        extension = 0;

        while( p.hasNext() ){

            puntoP  = (Punto)p.next();

            q       = this.puntosFrente.iterator();

            while( q.hasNext() ){

                nuevaDistancia  = this.distancia( puntoP, (Punto)q.next() );

                if( nuevaDistancia > extension ){
                    extension    = nuevaDistancia;
                }
            }
        }


        return( extension );
    }

    /**
     * Para pruebas
     * @param argv
     */
    public static void main( String argv[] ){
    // ------------------------------------------------------------------------

        FrentePareto frente, yTrue;

    // ------------------------------------------------------------------------

        frente  = new FrentePareto();
        yTrue   = new FrentePareto( "Ytrue.txt" );

        frente.cargarYTrue();
        yTrue.cargarYTrue();

        frente.imprimirFrente();

        System.out.println( "Distancia a Ytrue, " + frente.calcularDistanciaYtrueM1(yTrue) );
        System.out.println( "Distribucion, " + frente.calcularDistribucionM2( 0.001 ) );
        System.out.println( "Extension, " + frente.calcularExtensionM3() );
    }
}